package com.whz.governance.sensitive;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * nacos config uploader
 *
 * @author whz
 */
@Slf4j
public class SensitiveStoreUploader {
    private final SensitiveNacosConfig sensitiveNacosConfig;
    private final ConfigService configService;
    private final Map<String, AtomicBoolean> onlyMap = Maps.newConcurrentMap();
    private final Map<String, Set<String>> data = Maps.newConcurrentMap();


    public SensitiveStoreUploader(ConfigService configService, SensitiveNacosConfig sensitiveNacosConfig) {
        this.configService = configService;
        this.sensitiveNacosConfig = sensitiveNacosConfig;
    }

    /**
     * publish and override nacos config as json
     *
     * @param sensitiveEnum sensitive key type
     * @param sensitiveData sensitive key
     * @return true success of publishing
     */
    @SneakyThrows
    public boolean uploadOverride(final SensitiveEnum sensitiveEnum, final Set<String> sensitiveData) {
        String dataId = sensitiveNacosConfig.getDataId(sensitiveEnum);
        return uploadOverride(dataId, JSON.toJSONString(sensitiveData), ConfigType.JSON);
    }

    /**
     * publish and override nacos config as text
     *
     * @param dataId     nacos dataId
     * @param content    key to store
     * @param configType config type : json,text,properties,yaml
     * @return true success of publishing
     */
    @SneakyThrows
    public boolean uploadOverride(final String dataId, final String content, ConfigType configType) {
        String groupId = sensitiveNacosConfig.getGroupId();
        long timeout = sensitiveNacosConfig.getTimeOut();
        return configService.publishConfig(dataId, groupId, content, configType.getType());
    }

    /**
     * publish config and incremental updating key as json, not override it
     *
     * @param sensitiveEnum sensitive key type
     * @param sensitiveData sensitive key
     * @return true success of publishing
     */
    public boolean uploadContinue(final SensitiveEnum sensitiveEnum, final Set<String> sensitiveData) {
        String dataId = sensitiveNacosConfig.getDataId(sensitiveEnum);
        //======================== 增量添加配置
        Set<String> dataBy = getDataBy(dataId);
        dataBy.addAll(sensitiveData);
        return uploadContinue(dataId, ConfigType.JSON);
    }


    /**
     * publish config and incremental updating key, not override it
     *
     * @param dataId     nacos dataId
     * @param configType config type
     * @return true success of publishing
     */
    @SneakyThrows
    public boolean uploadContinue(final String dataId, ConfigType configType) {
        Assert.hasText(dataId, "dataId Cannot be blank.");
        String groupId = sensitiveNacosConfig.getGroupId();
        long timeout = sensitiveNacosConfig.getTimeOut();
        if (getOnlyBool(dataId).compareAndSet(false, true)) {
            // 监听nacos变更
            String config = configService.getConfigAndSignListener(dataId, groupId, timeout, new AbstractListener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    // update changed key
                    increaseUpdating(dataId, configInfo, false);
                }

                @Override
                public Executor getExecutor() {
                    return sensitiveNacosConfig.getExecutorService();
                }
            });
            // 首次更新本地缓存
            increaseUpdating(dataId, config, true);
        }
        return configService.publishConfig(dataId, groupId, JSON.toJSONString(getDataBy(dataId)), configType.getType());
    }

    private AtomicBoolean getOnlyBool(String key) {
        return onlyMap.computeIfAbsent(key, k -> new AtomicBoolean(false));
    }

    private void increaseUpdating(String dataId, String configInfo, boolean init) {
        if (!StringUtils.isEmpty(configInfo)) {
            log.info("increaseUpdating sensitive key:{}", configInfo);
            Collection<String> newData = JSON.parseArray(configInfo, String.class);
            //======================== 在更新中把nacos配置作为全量，初始化则合并
            if (!init) {
                getDataBy(dataId).clear();
            }
            getDataBy(dataId).addAll(newData);
        }
    }

    private Set<String> getDataBy(String key) {
        return data.computeIfAbsent(key, k -> Sets.newConcurrentHashSet());
    }

    /**
     * remove sensitive key
     *
     * @param sensitiveEnum sensitive key type
     * @return true remove success
     */
    @SneakyThrows
    public boolean removeConfig(final SensitiveEnum sensitiveEnum) {
        String dataId = sensitiveNacosConfig.getDataId(sensitiveEnum);
        return removeConfig(dataId);
    }

    @SneakyThrows
    public boolean removeConfig(final String dataId) {
        String groupId = sensitiveNacosConfig.getGroupId();
        return configService.removeConfig(dataId, groupId);
    }
}